/**
 * Title: Hyperbolic engine
 * Description: jFamilyTree Engine
 * Version: provider
 * Copyright: (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 * Update: Mon Mar 10 00:00:00 CEST 2008
 */
package jFamilyTree.control;

import jFamilyTree.core.AbstractLayerOut;
import jFamilyTree.core.Complex;
import jFamilyTree.model.Model;
import jFamilyTree.view.View;

/**
 * Commander executes commands
 * 
 * @author Bernard Bou
 */
public abstract class Commander
{
	/**
	 * Commands
	 */
	public static enum Command
	{
		REFRESH, HOME, NORTH, SOUTH, EAST, WEST, RADIAL, ZOOMIN, ZOOMOUT, EXPAND, SHRINK, WIDEN, NARROW, HYPERBOLICEDGE, TOOLTIP, TOOLTIPCONTENT, FOCUSHOVER
	};

	/**
	 * East/West shift step
	 */
	static private final double theXShiftStep = 0.1;

	/**
	 * North/south shift step
	 */
	static private final double theYShiftStep = 0.1;

	/**
	 * Sweep factor
	 */
	static private final double theSweepFactor = 1.1;

	/**
	 * Expansion factor
	 */
	static private final double theExpansionFactor = 1.1;

	// A C C E S S

	/**
	 * Get model
	 * 
	 * @return model
	 */
	abstract protected Model getModel();

	/**
	 * Get view
	 * 
	 * @return view
	 */
	abstract protected View getView();

	/**
	 * Get layout agent
	 * 
	 * @return layout agent
	 */
	abstract protected AbstractLayerOut getLayerOut();

	// H E L P E R S

	/**
	 * Set north orientation
	 */
	private void setNorth()
	{
		if (!changeOrientation(Complex.SOUTH))
			getView().setShift(0., theYShiftStep, false, true);
	}

	/**
	 * Set south orientation
	 */
	private void setSouth()
	{
		if (!changeOrientation(Complex.NORTH))
			getView().setShift(0., -theYShiftStep, false, true);
	}

	/**
	 * Set east orientation
	 */
	private void setEast()
	{
		if (!changeOrientation(Complex.EAST))
			getView().setShift(-theXShiftStep, 0., true, false);
	}

	/**
	 * Set west orientation
	 */
	private void setWest()
	{
		if (!changeOrientation(Complex.WEST))
			getView().setShift(+theXShiftStep, 0., true, false);
	}

	/**
	 * Set radial orientation
	 */
	private void setRadial()
	{
		changeOrientation(Complex.ZERO);
		getView().setShift(0., 0., false, false);
	}

	/**
	 * Change orientation
	 * 
	 * @param thisOrientation
	 *        orientation
	 * @return true if successful
	 */
	private boolean changeOrientation(Complex thisOrientation)
	{
		if (thisOrientation.equals(getLayerOut().getOrientation()))
			return false;
		getView().resetTransform();
		getLayerOut().setOrientation(thisOrientation);
		boolean isRadial = thisOrientation == Complex.ZERO;
		getLayerOut().setDefaultRootSweep(isRadial);
		getLayerOut().setDefaultChildSweep(isRadial);
		getLayerOut().layout(getModel().theTree.getRoot());
		return true;
	}

	/**
	 * Change Expansion by given factor
	 * 
	 * @param thisFactor
	 *        factor
	 */
	private void changeExpansion(double thisFactor)
	{
		double thisExpansion = getLayerOut().getExpansion();
		thisExpansion *= thisFactor;
		if (thisExpansion > .4)
			return;
		getView().resetTransform();
		getLayerOut().setExpansion(thisExpansion);
		getLayerOut().layout(getModel().theTree.getRoot());
	}

	/**
	 * Change sweep by given factor
	 * 
	 * @param thisFactor
	 *        factor
	 */
	private void changeSweep(double thisFactor)
	{
		double thisSweep = getLayerOut().getChildSweep();
		thisSweep *= thisFactor;
		if (thisSweep > Math.PI)
			return;
		getView().resetTransform();
		getLayerOut().setChildSweep(thisSweep);
		getLayerOut().layout(getModel().theTree.getRoot());
	}

	// D O

	/**
	 * Perform repaint
	 */
	private void doRefresh()
	{
		getView().repaint();
	}

	/**
	 * Perform north orient
	 */
	private void doNorth()
	{
		setNorth();
		getView().repaint();
	}

	/**
	 * Perform south orient
	 */
	private void doSouth()
	{
		setSouth();
		getView().repaint();
	}

	/**
	 * Perform east orient
	 */
	private void doEast()
	{
		setEast();
		getView().repaint();
	}

	/**
	 * Perform west orient
	 */
	private void doWest()
	{
		setWest();
		getView().repaint();
	}

	/**
	 * Perform tree radial orient
	 */
	private void doRadial()
	{
		setRadial();
		getView().repaint();
	}

	/**
	 * Perform zoom in
	 */
	private void doZoomin()
	{
		getView().setZoomFactor(1.2);
		getView().repaint();
	}

	/**
	 * Perform zoom out
	 */
	private void doZoomout()
	{
		getView().setZoomFactor(1 / 1.2);
		getView().repaint();
	}

	/**
	 * Perform reset
	 */
	private void doHome()
	{
		getView().applyNullTransform();
		getView().setShift(0., 0., false, false);
		getView().repaint();
	}

	/**
	 * Perform change expansion by given factor
	 * 
	 * @param thisFactor
	 *        factor
	 */
	private void doChangeExpansion(double thisFactor)
	{
		changeExpansion(thisFactor);
		getView().repaint();
	}

	/**
	 * Perform change sweep by given factor
	 * 
	 * @param thisFactor
	 *        factor
	 */
	private void doChangeSweep(double thisFactor)
	{
		changeSweep(thisFactor);
		getView().repaint();
	}

	/**
	 * Set edges rendering as hyperbolic
	 */
	private void doHyperbolicEdge()
	{
		getView().setHyperbolicEdges(null);
		getView().repaint();
	}

	/**
	 * Set tooltips
	 */
	private void doTooltip()
	{
		getView().setHasTooltip(null);
	}

	/**
	 * Set tooltip displays content flag
	 */
	private void doTooltipContent()
	{
		getView().setTooltipDisplaysContent(null);
	}

	/**
	 * Set focus on hover flag
	 */
	private void doFocusHover()
	{
		getView().setFocusOnHover(null);
	}

	// C O M M A N D P R O C E S S O R

	/**
	 * Command dispatcher
	 * 
	 * @param thisCommand
	 *        command
	 */
	public void execute(Command thisCommand)
	{
		switch (thisCommand)
		{
		case REFRESH:
			doRefresh();
			break;
		case HOME:
			doHome();
			break;
		case NORTH:
			doNorth();
			break;
		case SOUTH:
			doSouth();
			break;
		case EAST:
			doEast();
			break;
		case WEST:
			doWest();
			break;
		case RADIAL:
			doRadial();
			break;
		case ZOOMIN:
			doZoomin();
			break;
		case ZOOMOUT:
			doZoomout();
			break;
		case EXPAND:
			doChangeExpansion(theSweepFactor);
			break;
		case SHRINK:
			doChangeExpansion(1. / theSweepFactor);
			break;
		case WIDEN:
			doChangeSweep(theExpansionFactor);
			break;
		case NARROW:
			doChangeSweep(1. / theExpansionFactor);
			break;
		case HYPERBOLICEDGE:
			doHyperbolicEdge();
			break;
		case TOOLTIP:
			doTooltip();
			break;
		case TOOLTIPCONTENT:
			doTooltipContent();
			break;
		case FOCUSHOVER:
			doFocusHover();
			break;
		}
	}
}
